#include <glib.h>
#include <gio/gio.h>
#include "common.h"

#define SMART_MESSAGE "org.ofono.SmartMessaging"
#define SMART_MESSAGE_AGENT "org.ofono.SmartMessagingAgent"
#define AGENT_PATH "/test/smartmessaging"

static const char *vcard = "BEGIN:VCARD\n"
        "VERSION:2.1\n"
        "N;FN:alex\n"
        "TEL;WORK:02551887700\n"
        "END:VCARD";
static const char *recipient = "10089";
static const GMainLoop *main_loop;

static GDBusNodeInfo *introspection_data = NULL;

static const gchar introspection_xml[] = 
        "<node>"
        "   <interface name='org.ofono.SmartMessagingAgent'>"
        "      <method name='ReceiveBusinessCard'>"
        "         <arg type='ay' name='vcard' direction='in'/>"
        "         <arg type='a{sv}' name='info' direction='in'/>"
        "      </method>"
        "      <method name='Release'/>"
        "   </interface>"
        "</node>";

static void agent_released() {
    g_print("good bye cruel world, agent is release\n");
    g_main_loop_quit(main_loop);
}

static void vcard_received(GVariant *params) {
    g_print("vcard received, type is '%s'\n", g_variant_get_type(params));
    GVariant *vcard = g_variant_get_child_value(params, 0);
    gsize len = 0;
    const guchar *vcard_content = g_variant_get_fixed_array(vcard, &len, sizeof(guchar));
    hexdump(vcard_content, len, "vcard received");
    GVariant *info = g_variant_get_child_value(params, 1);
    iterate_dictionary(info);
    g_variant_unref(info);
    g_variant_unref(vcard);
}

static void handle_method_call(GDBusConnection *connect, const gchar *sender, const gchar *object_path, 
        const gchar *interface, const gchar *method_name, GVariant *params, 
        GDBusMethodInvocation *invocation, gpointer user_data) {
    g_print("handle method: sender %s, object path %s, interface %s, method %s\n", sender, object_path, interface, method_name);
    if (g_strcmp0(method_name, "Release") == 0) {
        agent_released();
    } else if (g_strcmp0(method_name, "ReceiveBusinessCard") == 0) {
        vcard_received(params);
    }
}

static const GDBusInterfaceVTable interface_vtable = {
    handle_method_call,
    NULL,
    NULL
};

static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) {
    g_print("on_bus_acquired name is '%s'\n", name);
    guint registration_id = g_dbus_connection_register_object(connection, AGENT_PATH, 
            introspection_data->interfaces[0], &interface_vtable, NULL, NULL, NULL);
    g_assert(registration_id > 0);
    g_print("on_bus_acquired registration_id is %d\n", registration_id);
}

static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) {
    g_print("on_name_acquired name is %s\n", name);
}

static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data) {
    g_print("on_name_lost name is %s\n", name);
}

static void on_send_cb(GObject *source, GAsyncResult *result, gpointer user_data) {
    GError *error = NULL;
    GDBusConnection *conn = G_DBUS_CONNECTION(source);
    GVariant *call_result = g_dbus_connection_call_finish(conn, result, &error);
    if (call_result == NULL) {
        g_print("error on send buisness card: %s\n", error->message);
        g_error_free(error);
        return;
    }
    const char *path = NULL;
    g_variant_get(call_result, "(o)", &path);
    g_print("business card object path is '%s'\n", path);
    g_variant_unref(call_result);
    //g_variant_unref((GVariant *) user_data);
}

static void on_register_cb(GObject *source, GAsyncResult *result, gpointer user_data) {
    g_print("on_register_cb lalala\n");
    GError *error = NULL;
    GDBusConnection *conn = G_DBUS_CONNECTION(source);
    GVariant *call_result = g_dbus_connection_call_finish(conn, result, &error);
    if (call_result == NULL) {
        g_print("error on register agent: %s\n", error->message);
        g_error_free(error);
        return;
    }
    g_variant_unref(call_result);
}

int main(int argc, char *argv[]) {
    g_type_init();
    GDBusConnection *conn = get_system_bus_connection();
    const gchar *modems[MODEM_COUNT_LIMIT];
    gsize modem_count = 0;
    get_modem_paths(modems, &modem_count);
    if (modem_count < 1) {
        g_print("no modems available.\n");
        return -1;
    }
    /* register agent to receive business card */
    GVariant *smart_messaging_agent_path = g_variant_new("(o)", AGENT_PATH);
    GError *error = NULL;
    introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, &error);
    if (introspection_data == NULL) {
        g_print("fuck parsing xml: %s\n", error->message);
        g_error_free(error);
        return 0;
    }
    const guint owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, SMART_MESSAGE_AGENT, G_BUS_NAME_OWNER_FLAGS_NONE, 
            on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL);
    g_dbus_connection_call(conn, OFONO_SERVICE_NAME, modems[0], SMART_MESSAGE, "RegisterAgent", 
            smart_messaging_agent_path, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL,
            (GAsyncReadyCallback) on_register_cb, NULL);

    g_print("lalalaala\n");
    main_loop = g_main_loop_new(NULL, FALSE);

    GVariantBuilder builder;
    g_variant_builder_init(&builder, ((const GVariantType *) "(say)"));
    g_variant_builder_add_value(&builder, g_variant_new_string(recipient));
    g_variant_builder_add_value(&builder, g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
                vcard, strlen(vcard), sizeof(guchar)));
    GVariant *in_params = g_variant_builder_end(&builder);
    g_print("shit shit shit\n");
    g_dbus_connection_call(conn, OFONO_SERVICE_NAME, modems[0],
            SMART_MESSAGE, "SendBusinessCard",
            in_params, G_VARIANT_TYPE("(o)"),
            G_DBUS_CALL_FLAGS_NONE, -1, NULL,
            (GAsyncReadyCallback) on_send_cb, (gpointer) in_params);

    g_main_loop_run(main_loop);

    g_variant_unref(smart_messaging_agent_path);
    g_bus_unown_name(owner_id);
    g_dbus_node_info_unref(introspection_data);
    for (int i = 0; i < modem_count; i++) {
        g_free(modems[i]);
    }
    return 0;
}
